package com.accessibleworlds.blind;

import java.util.ArrayList;
import java.util.Collections;

import org.json.JSONArray;

import com.accessibleworlds.R;
import com.accessibleworlds.report.ReportInformation;
import com.accessibleworlds.util.Connections;
import com.accessibleworlds.util.SharedVariables;
import com.accessibleworlds.util.TextToSpeeches;
import com.accessibleworlds.util.TextToSpeechesGoogle;
import com.accessibleworlds.util.TextToSpeechesPico;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;

/**
 * Represents the search report class for blind users.
 * @author Ayberk Özgür
 *
 */
public class BlindUserSearchReports extends Activity implements MultitouchGestureListener {
	private static final double EARTH_RADIUS = 6378100;
	
	private TextToSpeeches tts = null;
	private boolean ttsReady = false;
	private Thread connectionThread;
	private ArrayList<BlindLocation> spokenMenu = null;
	private int currentItem = 0;
	private int numberOfItems = 0;

	private double latitude, longitude;
	private LocationManager mlocManager;
	private LocationListener mlocListener;

	private boolean gotReports = false;

	private ProgressDialog progressDialog;

	private ArrayList<ReportInformation> allReports;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.blinduserpage);

		progressDialog = new ProgressDialog(this);
		progressDialog.setIndeterminate(true);
		progressDialog.setCancelable(false);

		spokenMenu = new ArrayList<BlindLocation>();
		connectionThread = new Thread(new Runnable() {
			public void run() {
				connection();
			}
		});
		connectionThread.start();

		mlocManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
		mlocListener = new BlindLocationListener();
		mlocManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
	}

	@Override
	public void onBackPressed() {
	}

	private void connection() {
		if("0".equalsIgnoreCase(getResources().getStringArray(R.array.languageValues)[SharedVariables.currentLanguage]))
			tts = new TextToSpeechesPico();
		else
			tts = new TextToSpeechesGoogle();
		ttsReady = true;
	}

	public void multitouchMotionEvent(int numberOfFingers, GestureType gestureType) {
		if(ttsReady){
			tts.stopSpeaking();
			switch(gestureType){
			case DOWN:
				if(numberOfItems > 0){
					currentItem = (currentItem + 1) % numberOfItems;
					tts.speak(BlindUserSearchReports.this, spokenMenu.get(currentItem).getSpokenString());
				}
				break;
			case UP:
				if(numberOfItems > 0){
					currentItem--;
					if(currentItem < 0)
						currentItem = numberOfItems - 1;
					tts.speak(BlindUserSearchReports.this, spokenMenu.get(currentItem).getSpokenString());
				}
				break;
			case LEFT:
				finish();
				break;
			case RIGHT:
				break;
			}
		}
	}

	public class GetReportsTask extends AsyncTask<Void, Integer, String>{
		private String[] takenData = null;

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			progressDialog.show();
		}

		@Override
		protected String doInBackground(Void... params) {
			if(Connections.serviceStatus()) {
				try {
					String url = Connections.URI+Connections.search;
					String [] parameters = {"accessCode"};
					String [] paramaterValues = {SharedVariables.accessCode};

					takenData = Connections.connection(url,parameters,paramaterValues);

					if("OK".equalsIgnoreCase(takenData[0])) {
						allReports = new ArrayList<ReportInformation>();
						for(int i=1;i<takenData.length;i++){
							JSONArray token = new JSONArray(takenData[i]);
							ReportInformation reportInformation = new ReportInformation(Integer.parseInt(token.getString(0)),
									token.getString(1),Integer.parseInt(token.getString(2)),
									token.getString(3),token.getString(4),
									Integer.parseInt(token.getString(5)), Integer.parseInt(token.getString(6)), Integer.parseInt(token.getString(7)));


							allReports.add(reportInformation);
						}
						publishProgress(1);
						return "OK";
					}
					else {
						publishProgress(2);
					}
				}
				catch (Exception ex) {
					publishProgress(2);
				}
			}
			else {
				publishProgress(0);
			}
			return "Fail";
		}

		protected void onProgressUpdate(Integer... progress) {
			if(progress[0] == 0) {
				progressDialog.dismiss();
				tts.speak(BlindUserSearchReports.this, "Connection problem. ");
			}
			else if(progress[0]==1) {
				String violationSpokenName;
				progressDialog.dismiss();

				spokenMenu.clear();
				for(int i=0;i<allReports.size();i++){
					if(allReports.get(i).type != 0){
						violationSpokenName = getResources().getStringArray(R.array.blind_violation_names)[allReports.get(i).type - 1];
						spokenMenu.add(new BlindLocation(violationSpokenName,distanceBetween(Math.toRadians(allReports.get(i).latitude/1000000.0), Math.toRadians(allReports.get(i).longitude/1000000.0), Math.toRadians(latitude), Math.toRadians(longitude)),0,0));
					}
				}
				
				Collections.sort(spokenMenu);
				
				//Find violations within 250 meters from user
				for(numberOfItems=0;numberOfItems<spokenMenu.size();numberOfItems++)
					if(spokenMenu.get(numberOfItems).getDistance() > 250)
						break;
				
				if(numberOfItems > 0)
					tts.speak(BlindUserSearchReports.this, "Found " + numberOfItems + " reports. Listing them now. " + spokenMenu.get(currentItem).getSpokenString());
				else
					tts.speak(BlindUserSearchReports.this, "Found no items within 250 meters from you.");
			}
			else if(progress[0]==2) {
				progressDialog.dismiss();
				tts.speak(BlindUserSearchReports.this, "Connection problem. ");
			}
		}

		@Override
		protected void onPostExecute(String result) {}
	}

	/* Class Blind Location Listener */
	private class BlindLocationListener implements LocationListener{
		public void onLocationChanged(Location loc){
			latitude = loc.getLatitude();
			longitude = loc.getLongitude();
			if(!gotReports){
				tts.speak(BlindUserSearchReports.this, "Finding nearby reports...");
				gotReports = true;
				runOnUiThread(new Runnable() {
					public void run() {
						new GetReportsTask().execute(new Void[]{});
					}
				});
			}
		}
		public void onProviderDisabled(String provider){
			while(!ttsReady);
			tts.speak(BlindUserSearchReports.this, "GPS is disabled. Please enable it to find nearby reports. ");
		}
		public void onProviderEnabled(String provider){
			while(!ttsReady);
			tts.speak(BlindUserSearchReports.this, "GPS is enabled.");

			mlocManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

			mlocListener = new BlindLocationListener();
			mlocManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
		}
		public void onStatusChanged(String provider, int status, Bundle extras){

		}
	}
	
	/**
	 * Calculates the great-circle distance between two geographical locations using the haversine formula.
	 * @param lat1 Latitude of the first location in radians
	 * @param lon1 Longitude of the first location in radians
	 * @param lat2 Latitude of the second location in radians
	 * @param lon2 Longitude of the second location in radians
	 * @return Distance between the two locations
	 */
	private double distanceBetween(double lat1, double lon1, double lat2, double lon2){
		double s1 = Math.sin((lat1-lat2)/2);
		s1 = s1*s1;
		double s2 = Math.sin((lon1-lon2)/2);
		s2 = s2*s2;
		double a = s1 + Math.cos(lat1)*Math.cos(lat2)*s2;
		return 2*EARTH_RADIUS*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));
	}
}
